import ctypes
import pandas as pd
import math
from util import *
from Task import Task
from queue import *
import threading


    
    
    
class Request:
    def init_for_sim(self, cloud_system, env, request_id, trace_data, print_level):
        self.sim_flage=True
        self.cloud_system=cloud_system
        #仿真环境
        self.env=env
        self.print_level=print_level
        #请求ID
        self.request_id=request_id
        #到达时间
        self.arrive_time=env.now
        self.complete_time=-1
        #原始数据，用于生成call graph
        self.trace_data=trace_data
        #总可靠性需求
        self.reliability=None
        #出现的task和副本已经实现的可靠性
        self.cur_reliability=None
        #设置一个模板，用于后续子可靠性和时间相关参数的求解
        self.request_base=None
        #所有任务数量
        # self.all_tasks_num = len(self.trace_data)
        
        self.trace_id=self.trace_data.iloc[0]["traceid"]
        self.service_name=self.trace_data.iloc[0]["service"]
        
        
        self.start_task_list=[]
        self.ms_kind_list=[]
        self.task_id_list=[]
        #for  our method
        
        self.task_id_to_obj={}
        self.task_id_to_replica_num={}
        #分区统计信息
        self.part_percent=None
        self.part_order=1
        self.part_0_number=0
        #动态比例信息
        self.dynamic_percent=None
        #完成任务数量
        self.finished_task_num = 0
        #完成标志
        self.back_task_num=0
        self.need_back_task_num=0
        self.all_task_num=0
        self.done_task_num=0
        self.cold_start_times=0
        self.hit_pre_deploy_times=0
        # self.back_flage=False
        
        #截止时间,一段时间，非时间点
        self.deadline=None
        #是否生成成功过的标志
        self.generate_flage=True
        self.succeed_flage=True
        self.reject_flage=False
        self.generate_sub_deadline_flage=False
        self.generate_replica_flage=False
        self.task_response_timeout_flage=False
        # self.fault_flage=False
        # self.within_deadline_flage=True
        self.without_failuer_flage=True
        self.response_time=None
        self.min_makespan=None
        self.max_makespan=None
        self.formal_flage=False
        #统计相关时间
        self.sum_cold_start_time=0
        self.sum_deal_time=0
        self.sum_wait_time=0
        self.sum_transform_time=0
        #生成任务序列
        self.generate_callgraph()
        if not list_is_sub_of_another_list(self.ms_kind_list, self.cloud_system.ms_kind_list):
            return 
        self.get_min_max_makespan()
        self.set_deadline()
        b=1
        self.update_request_statistic_info()
        a=1
        #计算分区比例
        self.part_percent=self.part_0_number/self.all_task_num
        c=1
         
    def init_for_analyze(self,request_id, trace_data, duration):
        self.sim_flage=False
        #请求ID
        self.request_id=request_id
        #原始数据，用于生成call graph
        self.trace_data=trace_data
        #单条数据的请求响应时间
        self.duration=duration
        #作为base 时多个请求的最小和最大响应时间
        self.min_deadline=duration
        self.max_deadline=duration
        #到达时间
        # self.arrive_time=trace_data.iloc[0]['timestamp']
        #所有任务数量
        # self.all_tasks_num = len(self.trace_data)
        #这个请求应用的名字
        self.service_name=self.trace_data.iloc[0]["service"]
        #是否生成成功的标志
        self.generate_flage=True
        #分区统计信息
        self.part_percent=None
        self.part_order=1
        self.part_0_number=0
        
        ###############关键信息#####################
        self.start_task_list=[]
        self.ms_kind_list=[]
        self.task_id_list=[]
        self.task_id_to_obj={}
        self.all_task_num=0
        self.max_task_num_per_call=None
        ####################################
        self.task_id_to_replica_num={}
        self.task_id_to_sub_deadline={}
        # self.GSMS_task_id_to_ms=None
        # self.GSMS_task_id_to_replica_num=None
        
        self.need_back_task_num=0
        self.request_call_num=0
        #生成任务序列
        self.generate_callgraph()
        self.update_request_statistic_info()
        self.part_percent=self.part_0_number/self.all_task_num
        
        
    def reset_for_our_method(self, reliability):
        self.reliability=reliability
        self.already_reliability=1
        self.already_num=0
        # self.scheduled_task_list=[]
        self.task_id_to_single_reliability={}
        self.replica_done_task_id_list=[]
        self.front_task_list=[]
        
    def add_attribute_for_our_method(self):
        self.already_reliability=1
        self.already_num=0
        self.front_task_list=[]
        
    #生成任务序列
    def generate_callgraph(self):
        df_temp=self.trace_data[(self.trace_data["um"] == "USER") & (self.trace_data["rpc_id"].isin(['0']) & (self.trace_data["rpctype"]=="http"))]
        # 获取要剔除的行的索引  
        indices_to_remove = df_temp.index  
        # 更新 trace_data，剔除 df_temp 的数据  
        self.trace_data = self.trace_data.drop(indices_to_remove) 
         
        if len(df_temp) ==0:
            print("request init wrong! (No enter trace info)")
            self.generate_flage=False
            return 
        
        for index, row in df_temp.iterrows():
            rpc_id=row["rpc_id"] 
            rpc_type=row["rpctype"]
            ms_kind=row["dm"] #哪种类型的微服务可以完成这个task
            rt=max(row["rt"],1)
            start_time_in_trace_data=row["timestamp"]
               
            #生成task名字，其中包含request名字
            pre_str=self.fix_rpc_id(rpc_id)
            task_id=f"|T_{pre_str}-{ms_kind}|"
            
            if task_id in self.task_id_list:  #不生成重复的id的task
                continue
            
                
            start_task = Task()
            #设置分区信息
            start_task.part_id=0
            self.part_0_number+=1
            #生成task
            if self.sim_flage==True:
                start_task.init_for_sim(self.env, self, task_id, None, ms_kind, rpc_id, rpc_type, rt,start_time_in_trace_data,self.print_level)
            else:
                start_task.init_for_analyze(self, task_id, None, ms_kind, rpc_id, rpc_type, rt,start_time_in_trace_data)
            #生成下一级task
            new_task_next_level_tasks_list = self.generate_next_level_tasks(start_task)
            start_task.set_sub_task_list(new_task_next_level_tasks_list)
            
            #统计关键信息
            if start_task.ms_kind not in self.ms_kind_list:
                self.ms_kind_list.append(start_task.ms_kind)
            self.start_task_list.append(start_task)
            self.task_id_list.append(task_id)
            self.task_id_to_obj[task_id]=start_task
            #统计需要返回的task数量
            if rpc_type!="mq":
                self.need_back_task_num+=1 
        return

    #生成下一级任务，并返回list
    def generate_next_level_tasks(self,parent_task):
        next_level_task_index_list=self.get_next_level_task_index(parent_task)
        
        next_level_tasks_list=[]
        for index in next_level_task_index_list:
            rpc_id=self.trace_data.loc[index, "rpc_id"]
            self.trace_data.loc[index, "rpc_id"]="None"
            rpc_type=self.trace_data.loc[index,"rpctype"]
            ms_kind=self.trace_data.loc[index,"dm"] #哪种类型的微服务可以完成这个task
            rt=max(self.trace_data.loc[index,"rt"],1)
            start_time_in_trace_data=self.trace_data.loc[index,"timestamp"]
            
            #生成task名字
            parent_task_id=parent_task.task_id if parent_task!=None else ""
            pre_str=self.fix_rpc_id(rpc_id)
            task_id=f"|T_{pre_str}-{ms_kind}{parent_task_id}"
            
            #不生成重复的id的task
            if task_id in self.task_id_list:  
                continue
            
            #生成新task    
            new_task = Task()
            #设置分区ID
            if rpc_type=="mq":
                new_task.part_id=self.part_order
                self.part_order+=1
            else:
                new_task.part_id=parent_task.part_id
                if new_task.part_id==0:
                    self.part_0_number+=1
                        
            if self.sim_flage==True:
                #开始初始化
                new_task.init_for_sim(self.env, self, task_id, parent_task, ms_kind, rpc_id, rpc_type, rt,start_time_in_trace_data,self.print_level)
            else:
                new_task.init_for_analyze(self, task_id, parent_task, ms_kind, rpc_id, rpc_type, rt,start_time_in_trace_data)
            
                
            #数据错误导致有可能是负数（需要修改）
            new_task.pre_time=max(round(new_task.start_time_in_trace_data-parent_task.start_time_in_trace_data),0)
            # new_task.suc_time=round(parent_task.start_time_in_trace_data+parent_task.deal_time-new_task.start_time_in_trace_data-new_task.deal_time)
            ##v1
            # if rpc_type!= "mq" and (new_task.pre_time<0 or new_task.suc_time<0):
            #     trace_id=self.trace_data.loc[index, "traceid"]
            #     if self.sim_flage:
            #         if self.print_level>11:
            #             print(f"generate_next_level_tasks wrong! (trace:{trace_id})")
            #     else:
            #         print(f"generate_next_level_tasks wrong! (trace:{trace_id})")
            #     continue
            
            # if rpc_type== "mq" and new_task.pre_time<0:
            #     trace_id=self.trace_data.loc[index, "traceid"]
            #     if self.sim_flage:
            #         if self.print_level>11:
            #             print(f"generate_next_level_tasks wrong! (trace:{trace_id})")
            #     else:
            #         print(f"generate_next_level_tasks wrong! (trace:{trace_id})")
            #     continue
                
            
            
            new_task_next_level_tasks_list=self.generate_next_level_tasks(new_task)
            new_task.set_sub_task_list(new_task_next_level_tasks_list)
            next_level_tasks_list.append(new_task)
            
            #统计关键信息
            if new_task.ms_kind not in self.ms_kind_list:
                self.ms_kind_list.append(new_task.ms_kind)
            self.task_id_list.append(task_id)
            self.task_id_to_obj[new_task.task_id]=new_task
        
        #删除用过的行
        self.trace_data=self.trace_data.drop(next_level_task_index_list)
        # self.trace_data.reset_index()
        return next_level_tasks_list

    def fix_rpc_id(self,rpc_id):
        parts = rpc_id.split('.')
        result_parts = []
        i = 0
        while i < len(parts):
            part = parts[i]
            if len(part) <5:
                result_parts.append(part)
            i += 1
        
        # 重新组合结果，确保不会以点开头或结尾
        result = '.'.join(result_parts)
        return result
    
    #从trace_data中获取下一级task的index
    def get_next_level_task_index(self,this_task):
        index_list=[]
        temp_data=self.trace_data[self.trace_data["um"]==this_task.ms_kind]
        for index, row in temp_data.iterrows():
            #满足三个条件则认为当前行对应子任务：rpc_id 包含关系，当前rpc_id是目标rpc_id最大的被包含的值，微服务类型一致
            if self.is_max_startswith(row["rpc_id"], this_task.rpc_id, row["um"]):    
                index_list.append(index)
        
        return index_list

    def is_max_startswith(self, next_level_rpcid, this_level_rpcid, um_kind):
        this_length=0
        if next_level_rpcid.startswith(this_level_rpcid):
            this_length=len(this_level_rpcid)
        else:
            return False
        
        if len(next_level_rpcid)==len(this_level_rpcid):
            return False
        #判断next_level_rpcid的最大包含是不是this_level_rpcid
        temp_data=self.trace_data[self.trace_data["um"]==um_kind]
        for index, row in temp_data.iterrows():
            if len(next_level_rpcid)>len(row["rpc_id"]) and len(row["rpc_id"])>this_length and next_level_rpcid.startswith(row["rpc_id"]):
                return False
        return True
                
    def back_one_task(self, fault_flage):
        if self.reject_flage==True:
            return
        if fault_flage==True:
            self.without_failuer_flage=False

        self.back_task_num+=1
        if self.back_task_num==self.need_back_task_num:
            self.complete_time=self.env.now
            self.response_time=self.complete_time-self.arrive_time
            # if self.complete_time-self.arrive_time>self.deadline or self.task_response_timeout_flage:
            #     self.within_deadline_flage=False
            self.cloud_system.end_one_request(self)
        return
    
    def back_with_reject(self):
        if self.reject_flage==False:
            self.reject_flage=True
            self.cloud_system.end_one_request(self)
        
        
        
    def reset_as_base(self):
        self.request_call_num=1
        self.max_task_num_per_call=self.all_task_num
        for task in self.start_task_list:
            task.reset_as_base(self, task.parent_task)
        
    def merge(self, request):
        self.request_call_num+=1
        for task in request.start_task_list:
            # if task.task_id=='|T_0-MS_19630|' and request.service_name=='S_130831269':
            #     a=1
            self.merge_task(task)
            
        self.merge_end()
        self.max_task_num_per_call=max(self.max_task_num_per_call, request.all_task_num)
        self.min_deadline=min(self.min_deadline, request.duration)
        self.max_deadline=max(self.max_deadline, request.duration)
        
    
    def merge_task(self,task):
        task_id=task.task_id
        if task_id not in self.task_id_list:
            #重置为模板
            if task.parent_task==None:
                #重置为适用于基础模板
                task.reset_as_base(self,None)
                #放入开始task列表
                self.start_task_list.append(task)
            else:
                parent_task=self.task_id_to_obj[task.parent_task.task_id]
                if task not in parent_task.sub_task_list:
                    parent_task.sub_task_list.append(task)
                task.reset_as_base(self,parent_task)
            
            #添加统计信息task id
            self.task_id_list.append(task_id)
            #添加统计信息 ms kind
            if task.ms_kind not in self.ms_kind_list:
                self.ms_kind_list.append(task.ms_kind)
            #记录task obj
            self.task_id_to_obj[task_id]=task
        else:
            base_task=self.task_id_to_obj[task_id]
            base_task.merge(task) 
        #当前融合后，开始融合子任务   
        if len(task.sub_task_list)>0:
            for sub_task in task.sub_task_list:
                self.merge_task(sub_task)
        
    
    def merge_end(self):
        for task in self.task_id_to_obj.values():
            task.merge_end()
        return
    

    
    def get_ms_kind_list(self):
        return self.ms_kind_list
        

    def set_deepcopy_request_base(self,request_base):
        self.request_base=request_base
    
    #由于出现task中的子task不存在，因此需要更新base中的信息（当前为模板request）
    def update_base_info(self, task):
        task_base=self.task_id_to_obj[task.task_id]

        #task 需要更新子task 列表的值（只有对应那一个改变）
        for index in range(len(task_base.sub_task_list)-1, -1, -1):
            task_t=task_base.sub_task_list[index]
            if task_t.task_id not in task.request.task_id_list:
                task_base.sub_task_list.pop(index)
                
        self.update_request_statistic_info()
        return
    
    
    #开始任务到来，即可知道有哪些base中的start task没有
    def update_base_start_task_list(self,task):
        update_flage=False
        for index in range(len(self.start_task_list)-1, -1, -1):
            task_t=self.start_task_list[index]
            if task_t.task_id not in task.request.task_id_list:
                self.start_task_list.pop(index)
                update_flage=True
        if update_flage==True:
            self.update_request_statistic_info()
                
    def update_request_statistic_info(self, task=None):
        #返回不包括task在内的子任务数量（多级子任务也包含）
        if task==None:
            # self.start_task_list 这个不能重置
            self.ms_kind_list=[]
            self.task_id_list=[]
            self.task_id_to_obj={}
            self.all_task_num=0
            
            for task_t in self.start_task_list:
                self.ms_kind_list.append(task_t.ms_kind)
                self.task_id_list.append(task_t.task_id)
                self.task_id_to_obj[task_t.task_id]=task_t
                assert task_t != None
                self.update_request_statistic_info(task_t)
                
            self.ms_kind_list=list(set(self.ms_kind_list))
            self.all_task_num=len(self.task_id_list)
            return 
        else:
            task.all_sub_task_num=0
            for task_t in task.sub_task_list:
                self.ms_kind_list.append(task_t.ms_kind)
                self.task_id_list.append(task_t.task_id)
                self.task_id_to_obj[task_t.task_id]=task_t
                assert task_t != None
                task.all_sub_task_num=task.all_sub_task_num+self.update_request_statistic_info(task_t)+1
            return task.all_sub_task_num
            
            
        
    # #递归更新所有task的子任务的数量，对于request则更新所有任务的数量
    # def update_all_sub_task_num(self):
    #     self.all_task_num=0
    #     for task_t in self.start_task_list:
    #         task_t.update_all_sub_task_num()
    #         self.all_task_num=self.all_task_num+1+task_t.all_sub_task_num
    
    
    
    def get_min_max_makespan(self):
        self.min_makespan=0
        self.max_makespan=0
        for task in self.start_task_list:
            min_end_time, max_end_time=task.get_expect_end_time(min_start_time=0, max_start_time=0, cloud_system=self.cloud_system)
            self.min_makespan=max(self.min_makespan, min_end_time)
            self.max_makespan=max(self.max_makespan, max_end_time)
        return
    
    def set_deadline(self):
        self.deadline=self.min_makespan+(self.max_makespan-self.min_makespan)*self.cloud_system.args.deadline_factor
    
    def set_reliability(self, reliability):
        #reliability是来自于系统需求
        self.reliability=reliability
        

    def get_task_num_and_replica_num(self):
        replica_num=0
        assert self.all_task_num==len(self.task_id_to_obj.values())
        for task_obj in self.task_id_to_obj.values():
            replica_num+=task_obj.replica_num
        return self.all_task_num, replica_num
    
    #完成一个task
    def done_one_task(self):
        self.done_task_num+=1
        return
    
    #判断是否完成了所有的task
    def is_all_task_done(self):
        if self.done_task_num==self.all_task_num:
            return True
        else:
            return False
# for task in self.start_task_list:
        #     self.ms_kind_list.append(task.ms_kind)
        #     self._statistic_ms_kind_in_subtask(task)
        # #去重
        # self.ms_kind_list=list(set(self.ms_kind_list))
        # return self.ms_kind_list
        
    # def _statistic_ms_kind_in_subtask(self, task):
    #     for sub_task in task.sub_task_list:
    #         self.ms_kind_list.append(sub_task.ms_kind)
    #         self._statistic_ms_kind_in_subtask(sub_task)
    #     return
    
    # def set_task_response_timeout(self):
    #     self.task_response_timeout_flage=True